﻿using System;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;

using Sce.Psm;

namespace CDude.Vita
{
    internal partial class VitaCOM
    {
        private static readonly string APP_VERSION = Assembly.GetExecutingAssembly().GetName().Version.ToString();
        private const string ARGUMENTS_NOT_VALID = "The arguments specified for this command are not valid.";
        private static readonly string COPYRIGHT = ((AssemblyCopyrightAttribute)Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyCopyrightAttribute), false)[0]).Copyright;
        private const string DEVICE_NOT_VALID = "The device you specified is not valid.";
        private const string DEVICE_OFFLINE = "The device you specified is offline.";
        private const string ERROR_OUT_FILE_PATH = "error_log.txt";
        private static readonly string MACHINE_NAME = System.Environment.MachineName;
        private const string NOT_CONNECTED = "You are not connected to a device.";

        //Check whether the program is connected to a device
        private static bool IsConnected()
        {
            try
            {
                if (deviceid == 8 || !PsmDevice.ListDevices()[deviceid].Online)
                {
                    com_Disconnect(false);
                    Console.WriteLine(NOT_CONNECTED);
                    return false;
                }
            }
            catch
            {
                return false;
            }

            return true;
        }

        //Check whether the specified device is valid
        private static bool IsValidDevice(short id)
        {
            PsmDevice[] devices = PsmDevice.ListDevices();
            if (devices.Length == 0 || id > devices.Length - 1 || id < 0)
            {
                Console.WriteLine(DEVICE_NOT_VALID);
                com_Disconnect(false);
                return false;
            }

            return true;
        }

        //Listen to user input
        private static void Listen()
        {
            Console.Write("\nVITACOM> ");
            string queryi = Console.ReadLine();
            string[] query = Regex.Matches(queryi, @"(?<sel>\w+)|\""(?<sel>[\w\s]*)""").Cast<Match>().Select(sel => sel.Groups["sel"].Value).ToArray();
            string[] queryl = query.Select(val => val.ToLower()).ToArray();
            Console.WriteLine();

            try
            {
                switch (queryl[0])
                {
                    case "connect":
                        if (query.Length > 1)
                            com_Connect(query[1]);
                        else
                            Console.WriteLine(ARGUMENTS_NOT_VALID);
                        break;
                    case "details":
                        if (query.Length > 1)
                            com_Details(query[1]);
                        else
                            Console.WriteLine(ARGUMENTS_NOT_VALID);
                        break;
                    case "disconnect":
                        com_Disconnect(true);
                        break;
                    case "exit":
                        com_Exit();
                        break;
                    case "help":
                        if (query.Length > 1)
                            help(query[1]);
                        else
                            help();
                        break;
                    case "kill":
                        com_Kill();
                        break;
                    case "launch":
                        string queryil = queryi.ToLower();
                        bool debug = queryil.Contains(" -d");
                        bool profile = queryil.Contains(" -p");
                        bool keepnet = queryil.Contains(" -c");
                        bool echo = queryil.Contains(" -e");

                        if (query.Length == 2)
                            com_Launch(query[1], debug, profile, keepnet, null, echo);
                        else if (query.Length > 2)
                            com_Launch(query[1], debug, profile, keepnet, query[2], echo);
                        else
                            Console.WriteLine(ARGUMENTS_NOT_VALID);
                        break;
                    case "list":
                        com_List();
                        break;
                    case "pack":
                        if (query.Length > 2)
                            com_Pack(query[1], query[2]);
                        else
                            Console.WriteLine(ARGUMENTS_NOT_VALID);
                        break;
                    case "uninstall":
                        if (query.Length > 1)
                            com_Uninstall(query[1]);
                        else
                            Console.WriteLine(ARGUMENTS_NOT_VALID);
                        break;
                    case "upload":
                        if (query.Length > 2)
                            com_Upload(query[1], query[2]);
                        else
                            Console.WriteLine(ARGUMENTS_NOT_VALID);
                        break;
                    case "":
                        Console.SetCursorPosition(Console.CursorLeft, Console.CursorTop - 1);
                        break;
                    default:
                        help();
                        break;
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("An error occurred while executing the command you specified.");
                SaveException(ex);
            }
        }

        private static void Main(string[] args)
        {
            Console.WriteLine("\nVitaCOM version {0}\n{1}\nOn computer: {2}", APP_VERSION, COPYRIGHT, MACHINE_NAME);
            while (true) Listen();
        }

        //Print detailed information about a command
        private static void PrintHelp(string description, string syntax, string[] arguments, string[] argumentdescriptions, string extrainfo, string[] examples)
        {
            if (description.Length > 76) description = description.Insert(76, "\n   ");

            string zargs = null;
            if (arguments != null)
            {
                for (int i = 0; i < arguments.Length; i++)
                {
                    if (argumentdescriptions[i].Length > 76) argumentdescriptions[i].Insert(76, "\n" + Tabs(" ", 21));
                    zargs += "    " + Tabs(arguments[i], 17) + argumentdescriptions[i] + "\n\n";
                }
            }

            if (extrainfo != null) extrainfo = "    " + extrainfo + "\n\n";

            string zexamples = null;
            if (examples != null)
                for (int i = 0; i < examples.Length; i++)
                    zexamples += "\n    " + examples[i];
            Console.WriteLine("    {0}\n\n" +
                              "Syntax:  {1}\n\n{2}{3}" +
                              "Example:\n{4}", description, syntax, zargs, extrainfo, zexamples);
        }

        //Save exception data to a file
        private static void SaveException(Exception ex)
        {
            try
            {
                string sout = ex.Message + "\r\n" + ex.StackTrace;
                using (StreamWriter sw = new StreamWriter(ERROR_OUT_FILE_PATH))
                    sw.Write(sout);

                Console.WriteLine("Saved stack trace to {0}!", ERROR_OUT_FILE_PATH);
            }
            catch (Exception exx)
            {
                Console.WriteLine("Could not save stack trace to file! {0}" + exx.Message);
                Console.WriteLine(exx.Message + "\n" + exx.StackTrace);
            }
        }

        //Insert tabs at the beginning of a string
        private static string Tabs(string input, int length)
        {
            string outpp = input;

            for (int i = 0; i < length - input.Length; i++)
                outpp += ' ';

            return outpp;
        }
    }
}